package cn.stylefeng.guns.sys.core.minio;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import org.springframework.beans.factory.InitializingBean;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * Minio文件服务器模板工具类
 *
 * @author zhushengjie
 * @date 2022-08-17
 */
public class MinioTemplate implements InitializingBean {

    private final MinioProperties minioProperties;

    private AmazonS3 amazonS3;

    public MinioTemplate(MinioProperties minioProperties) {
        this.minioProperties = minioProperties;
    }

    /**
     * 创建bucket
     *
     * @param bucketName bucket名称
     */
    public void createBucket(String bucketName) {
        if (!this.amazonS3.doesBucketExistV2(bucketName)) {
            this.amazonS3.createBucket(bucketName);
        }

    }

    /**
     * 获取全部bucket
     * <p>
     * https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
     */
    public List<Bucket> getAllBuckets() {
        return this.amazonS3.listBuckets();
    }

    /**
     * 根据bucketName获取信息
     *
     * @param bucketName bucket名称
     */
    public Optional<Bucket> getBucket(String bucketName) {
        return this.amazonS3.listBuckets().stream().filter((b) -> {
            return b.getName().equals(bucketName);
        }).findFirst();
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public void removeBucket(String bucketName) {
        this.amazonS3.deleteBucket(bucketName);
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @return MinioItem 列表
     */
    public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix) {
        ObjectListing objectListing = this.amazonS3.listObjects(bucketName, prefix);
        return new ArrayList(objectListing.getObjectSummaries());
    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param minutes    过期时间 <=7
     * @return url
     */
    public String getObjectURL(String bucketName, String objectName, int minutes) {
        return this.getObjectURL(bucketName, objectName, Duration.ofMinutes((long)minutes));
    }

    public String getObjectURL(String bucketName, String objectName, Duration expires) {
        return this.getObjectURL(bucketName, objectName, expires, HttpMethod.GET);
    }

    public String getPutObjectURL(String bucketName, String objectName, int minutes) {
        return this.getPutObjectURL(bucketName, objectName, Duration.ofMinutes((long)minutes));
    }

    public String getPutObjectURL(String bucketName, String objectName, Duration expires) {
        return this.getObjectURL(bucketName, objectName, expires, HttpMethod.PUT);
    }

    public String getObjectURL(String bucketName, String objectName, int minutes, HttpMethod method) {
        return this.getObjectURL(bucketName, objectName, Duration.ofMinutes((long)minutes), method);
    }

    public String getObjectURL(String bucketName, String objectName, Duration expires, HttpMethod method) {
        Date expiration = Date.from(Instant.now().plus(expires));
        URL url = this.amazonS3.generatePresignedUrl((new GeneratePresignedUrlRequest(bucketName, objectName)).withMethod(method).withExpiration(expiration));
        return url.toString();
    }

    public String getObjectURL(String bucketName, String objectName) {
        URL url = this.amazonS3.getUrl(bucketName, objectName);
        return url.toString();
    }

    /**
     * 获取文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     */
    public S3Object getObject(String bucketName, String objectName) {
        return this.amazonS3.getObject(bucketName, objectName);
    }

    /**
     * 上传文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public void putObject(String bucketName, String objectName, InputStream stream) throws IOException {
        this.putObject(bucketName, objectName, stream, (long)stream.available(), "application/octet-stream");
    }

    public void putObject(String bucketName, String objectName, String contextType, InputStream stream) throws IOException {
        this.putObject(bucketName, objectName, stream, (long)stream.available(), contextType);
    }

    /**
     * 上传文件
     *
     * @param bucketName  bucket名称
     * @param objectName  文件名称
     * @param stream      文件流
     * @param size        大小
     * @param contextType 类型
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(size);
        objectMetadata.setContentType(contextType);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, stream, objectMetadata);
        putObjectRequest.getRequestClientOptions().setReadLimit(Long.valueOf(size).intValue() + 1);
        return this.amazonS3.putObject(putObjectRequest);
    }

    /**
     * 获取文件信息, 如果抛出异常则说明文件不存在
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
     */
    public S3Object getObjectInfo(String bucketName, String objectName) {
        return this.amazonS3.getObject(bucketName, objectName);
    }
    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
     */
    public void removeObject(String bucketName, String objectName) {
        this.amazonS3.deleteObject(bucketName, objectName);
    }

    /**
     * 生成唯一文件名
     * @param objectName
     * @return
     */
    public static String getFileName(String objectName){
        //判断文件最后一个.所在的位置
        int lastIndexOf = objectName.lastIndexOf(".");
        if(lastIndexOf == -1){
            return String.format("%s_%s",objectName,System.currentTimeMillis());
        }else {
            //获取文件前缀
            String filePrefix = objectName.substring(0,objectName.lastIndexOf("."));
            //获取文件后缀
            String fileSuffix = objectName.substring(objectName.lastIndexOf(".")+1);
            //组成唯一文件名
            return String.format("%s_%s.%s",filePrefix,System.currentTimeMillis(),fileSuffix);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ClientConfiguration clientConfiguration = new ClientConfiguration();
        AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(this.minioProperties.getEndpoint(), this.minioProperties.getRegion());
        AWSCredentials awsCredentials = new BasicAWSCredentials(this.minioProperties.getAccessKey(), this.minioProperties.getSecretKey());
        AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
        this.amazonS3 = (AmazonS3)((AmazonS3ClientBuilder)((AmazonS3ClientBuilder)((AmazonS3ClientBuilder)((AmazonS3ClientBuilder)((AmazonS3ClientBuilder) AmazonS3Client.builder().withEndpointConfiguration(endpointConfiguration)).withClientConfiguration(clientConfiguration)).withCredentials(awsCredentialsProvider)).disableChunkedEncoding()).withPathStyleAccessEnabled(this.minioProperties.getPathStyleAccess())).build();
    }

}
